import Animator from '@ohos.animator';
import prompt from '@system.prompt';

//用以计算方块移动
const dirArr = {
    'left': {d:1,s:1},
    'right': {d:-1,s:2},
    'up': {d:1,s:1},
    'down': {d:-1,s:2}
}

//记录当前空位置
var nullPlace = []

//让分数流动的计时器
var scoreTimer = undefined
var score = 0

//记录当前操作后的正确数据
var b_ = [[,,,],[,,,],[,,,]]

//创建方块计时器
var createTimer

//目前最大数字
var nowMax = 1

//创建随机数组，随机抽取
var createArr = [1,2,4]

//创建方块数目
var createNum = 1

//可移动
var moveTimer = undefined;

export default {
    data: {
        power: 0,
        bgHeight: 300,
        boxHeight: 0,
        boxWidth: 0,
        boxes: [[,,,],[,,,],[,,,],[,,,]],
        canMove: true,
        animationNum: 0,
        nowScore: 0,
        gameEnd: false,
        endText: '游戏结束',
        endOpacity: 0,
        maxNum: 1,
        maxLevel: 1,
        remotePower: 0
    },

    toLevel(newV, oldV){
        //当对方最大数字变化是，计算等级
        this.maxLevel = Math.floor(Math.LOG2E*Math.log(newV))
    },

    async onShow(){
        this.$watch('maxNum', 'toLevel')

        //订阅service用于接收对方设备的数据，同时发送数据给对方
        let res = await FeatureAbility.subscribeAbilityEvent({
            bundleName: 'com.example.funny1024',
            abilityName: 'com.example.funny1024.connect',
            messageCode: 1,
            abilityType: 0,
            syncOption: 0
        }, this.connectCallBack)
        if(res == 'true'){
            //订阅成功，游戏开始
            //由于页面加载需要时间，如果马上获取尺寸会获取失败
            setTimeout(()=>{
                this.start()
            }, 200)
            prompt.showToast({message:'游戏开始啦'})
        }else{
            prompt.showToast({message:'连接失败，无法游戏'})
        }
        this.start()
    },


    connectCallBack(data){
        console.warn(data)
        //收到对方设备数据，解析修改页面内容
        let msg = JSON.parse(data).data
        if(msg.create != undefined){
            //创建方块
            createNum += msg.create
            this.createBox()
        }
        if(msg.updateMax != undefined){
            //更新对方最大值
            let ani = this.$refs.remoteBox.animate([
                {transform: {scale: '1'}, offset: 0},
                {transform: {scale: '1.2'}, offset: 0.5},
                {transform: {scale: '1'}, offset: 1}],
                {duration: 300, fill: 'forwards'})
            //播放更新动画，增加抖动效果
            ani.play()
            //当方块变成最大值时修改其值
            setTimeout(()=>{this.maxNum = msg.updateMax}, 150)

        }
        if(msg.updatePower != undefined){
            //修改对方当前能量
            this.remotePower = msg.updatePower
        }
        if(msg.end != undefined){
            //对方胜利或失败，处理信息
            this.endText = msg.end ? '对方已达成目标' : '对方已失败'
            this.endOpacity = 0
            this.showEnd()
        }
    },

    start(){
        //获取背景宽度，以达成正方形区域
        let size = this.$refs.bg.getBoundingClientRect()
        this.bgHeight = size.width

        //计算每个box的宽高，对两边10px，中间间隔10px，故减去50px
        this.boxHeight = (size.width - 50) / 4
        this.boxWidth = (size.width - 50) / 4

        //随机生成第一个方块
        let x = Math.floor(Math.random() * 4)
        let y = Math.floor(Math.random() * 4)
        let boxes = [[,,,],[,,,],[,,,],[,,,]]
        for(let i=0;i<4;i++){
            for(let j=0;j<4;j++){
                if(i == x && j == y) boxes[i][j] = {v:1}
                else{
                    boxes[i][j] = {v:0}
                    //将空位置存入数组中
                    nullPlace.push(i+','+j)
                }
                //计算方块位置
                boxes[i][j].x = j * this.boxWidth + (j+1)*10
                boxes[i][j].y = i * this.boxHeight + (i+1)*10
            }
        }
        //复制一份
        b_ = JSON.parse(JSON.stringify(boxes))
        this.boxes = boxes
    },

    swipe(event){
        console.warn('swipe')
        //游戏结束或者还在播放移动动画或生成动画，无法移动
        if(this.gameEnd) return
        if(!this.canMove) return

        //禁止两次紧接的移动
        this.canMove = false
        if(moveTimer != undefined) clearInterval(moveTimer)

        //获取滑动方向
        let dir = event.direction

        //获取计算方向
        let dArr = dirArr[dir]
        let boxes = this.boxes

        for(let i=0;i<4;i++){
            for(let j=0;j<4;j++){
                //将本次所有方块的已结合属性置false
                b_[i][j].c = false
            }
        }

        //置零分数叠加，分数倍率
        let addScore = 0
        let multi = 0

        //动画记录数组
        let animates = []
        let tMax = nowMax

        for(let i = 0;i < 4;i++){
            //获得行或列的起始计算位置
            //对于左滑，不需要计算第一列(s=1)，对于右滑，不需要计算第四列(s=2)；上下滑同理
            let s = dArr.s

            //按着移动反方向逐个方块进行判断，直到尾部
            while(s >= 0 && s < 4){
                if(dir == 'left' || dir == 'right'){
                    //左右滑动
                    //当前块为空，没有数据，直接计算下一方块
                    if(b_[i][s].v == 0){
                        s+=dArr.d
                        continue
                    }

                    //计算移动方向到哪有非空物体
                    //初始为前一个方块
                    let nx = s - dArr.d
                    while(nx >=0 && nx < 4 && b_[i][nx].v == 0) nx -= dArr.d

                    //此时得到 nx 为前一非空方块的横坐标

                    //判断是否超出位置
                    //如果未超出，判断当前方块数字与nx所在方块位置数字是否一纸，一致则合并
                    if(nx >= 0 && nx < 4 && !b_[i][nx].c && b_[i][nx].v == b_[i][s].v){
                        //合并方块，c记录为true，代表有合并动画
                        animates.push({
                            x1: s, y1: i, x2: nx, y2: i, c: true, way:'x'
                        })

                        //将前方块更新为新的两倍值
                        b_[i][nx].v = 2 * b_[i][nx].v
                        //将当前方块置空
                        b_[i][s].v = 0
                        //将前方块c记录为true，防止连续合并
                        b_[i][nx].c = true

                        //添加分数
                        addScore +=  b_[i][nx].v
                        multi ++

                        //修改最大值
                        if(b_[i][nx].v > tMax) tMax = b_[i][nx].v

                        //添加空位置
                        nullPlace.push(i+','+s)
                    }else if(nx + dArr.d != s){
                        //不合并，仅移动
                        animates.push({
                            x1: s, y1: i,x2: nx + dArr.d, y2: i, way:'x'
                        })

                        //修改移动到的位置的方块值为当前方块的值
                        b_[i][nx + dArr.d].v = b_[i][s].v
                        //当前方块置空
                        b_[i][s].v = 0

                        //将原空位置的坐标更新为当前坐标
                        nullPlace[nullPlace.indexOf(i+','+(nx+dArr.d))] = i+','+s
                    }
                }
                else
                {
                    //上下滑动
                    //原理同左右滑动
                    if(b_[s][i].v == 0){
                        s+=dArr.d
                        continue
                    }
                    let ny = s - dArr.d
                    while(ny >=0 && ny < 4 && b_[ny][i].v == 0) ny -= dArr.d
                    if(ny >= 0 && ny < 4 && !b_[ny][i].c && b_[ny][i].v == b_[s][i].v){
                        animates.push({
                            x1: i,x2: i, y1: s, y2: ny, c: true, way:'y'
                        })
                        b_[ny][i].v = 2 * b_[ny][i].v
                        b_[ny][i].c = true
                        b_[s][i].v = 0
                        addScore += b_[ny][i].v
                        multi ++
                        if(b_[ny][i].v > tMax) tMax = b_[ny][i].v
                        nullPlace.push(s+','+i)
                    }else if(ny + dArr.d != s){
                        animates.push({
                            x1: i,x2: i, y1: s, y2: ny + dArr.d, way:'y'
                        })
                        b_[ny + dArr.d][i].v = b_[s][i].v
                        b_[s][i].v = 0
                        nullPlace[nullPlace.indexOf((ny+dArr.d) + ',' + i)] = s+','+i
                    }
                }
                s+=dArr.d
            }
        }
        //this.printB()
        //计算分数总加成与能量加成
        score += addScore * multi
        this.power += addScore * multi * 2

        //如果最大数字发生变化，告知对方
        if(tMax > nowMax){
            nowMax = tMax
            this.sendToRemote('{"updateMax":'+ nowMax +'}')
        }

        //如果无方块移动，则不创建新的方块
        this.animationNum = animates.length
        if(animates.length == 0) {this.canMove = true;return}

        //播放方块移动动画
        animates.forEach(item=>{
            let s,e
            //计算起始与终止位置
            if(item.x1 != item.x2){
                s = item.x1 * this.boxWidth + (item.x1+1)*10;
                e = item.x2 * this.boxWidth + (item.x2+1)*10;
            }else{
                s = item.y1 * this.boxHeight + (item.y1+1)*10;
                e = item.y2 * this.boxHeight + (item.y2+1)*10;
            }
            let animator = Animator.createAnimator({
                begin: s, end: e,
                fill:'forwards',duration:400,easing:'friction'})
            animator.onframe = function (value) {
                //每一帧更新方块位置
                boxes[item.y1][item.x1][item.way] = value
            }
            animator.onfinish = () => {
                //播放结束
                boxes[item.y1][item.x1].v = b_[item.y1][item.x1].v
                boxes[item.y1][item.x1].x = item.x1 * this.boxWidth + (item.x1 + 1) * 10
                boxes[item.y1][item.x1].y = item.y1 * this.boxHeight + (item.y1 + 1) * 10
                boxes[item.y2][item.x2].v = b_[item.y2][item.x2].v
                //判断是否需要播放合并动画
                if(item.c){
                    let ani = this.$element('boxes_' + item.x2 + '_' + item.y2).animate([
                        {transform: {scale: '1'},offset:0},
                        {transform: {scale: '1.2'}, offset:0.5},
                        {transform: {scale: '1'},offset:1}],
                        {duration:200,fill:'forwards',easing:'friction'})
                    ani.play()
                    ani.onfinish = ()=>{
                        this.animationNum--
                    }
                }else this.animationNum--
            }
            animator.play()
        })

        //计算流动速度，插值越大流动越快
        let refreshTime = 1000 / (score - this.nowScore)
        if(score != this.nowScore && scoreTimer == undefined){
            scoreTimer = setInterval(()=>{
                if(score > this.nowScore){
                    this.nowScore ++
                }else {
                    clearInterval(scoreTimer)
                    scoreTimer = undefined
                }
            }, refreshTime < 10 ? 10 : refreshTime)
        }

        //判断是否成功合成出1024
        if(nowMax >= 1024){
            this.endText = '达成目标'
            this.endOpacity = 0
            this.showEnd()
            this.sendToRemote('{"end":true}')
            return
        }

        //生成一个新的方块
        createNum = 1
        this.createBox()

        //判断能量是否满额
        if(this.power >= 100){
            //播放满格动画
            let ani = this.$refs.powerLine.animate([
                {opacity: '1', transform: {scale: '1'}, offset: 0},
                {opacity: '0', transform: {scale: '1.2'}, offset: 0.25},
                {opacity: '1', transform: {scale: '1'}, offset: 0.5},
                {opacity: '0', transform: {scale: '1.2'}, offset: 0.75},
                {opacity: '1', transform: {scale: '1'}, offset: 1}],
                {duration: 1000})
            ani.play()

            //计算溢出了多少方块
            let k = Math.floor(this.power / 100)
            this.power = this.power % 100

            //告知对方生成方块
            this.sendToRemote('{"create":' + k + '}')
        }

        //告知对方更新能量
        this.sendToRemote('{"updatePower":' + this.power + '}')
    },

    createBox(){
        createTimer = setInterval(()=>{
            //当移动动画全部结束后，才可以生成方块
            if(this.animationNum != 0) return
            clearInterval(createTimer)
            while(createNum > 0) {
                createNum --

                //从空位置中随机抽取一个
                let r = Math.floor(Math.random() * nullPlace.length)

                //若当前已无空位置，但仍要创建方块，则失败
                if(nullPlace.length == 0){
                    this.endText = '游戏结束'
                    this.endOpacity = 0
                    this.showEnd()
                    this.sendToRemote('{"end":false}')
                    return
                }
                //得到坐标
                let p = nullPlace[r].split(',')

                //计算其生成的起始数值
                let t = createArr.indexOf(nowMax)
                t = nowMax > 4 ? 3 : (t == -1 ? 0 : t)
                let v = createArr[Math.floor(Math.random() * t)]

                //将方块渲染到界面中
                this.boxes[parseInt(p[0])][parseInt(p[1])].v = v
                b_[parseInt(p[0])][parseInt(p[1])].v = v

                //删除这个空位置
                nullPlace.splice(r, 1)

                //如果空位置变为0且无任何可合成方块，则失败
                if (nullPlace.length == 0 && this.checkIfEnd()) {
                    this.endText = '游戏结束'
                    this.endOpacity = 0
                    this.showEnd()
                    this.sendToRemote('{"end":false}')
                    return
                }
            }
            this.canMove = true
        }, 100)

        //规定3s后必须可以再次移动，防止卡死
        moveTimer = setTimeout(()=>{this.canMove = true}, 3000)
    },

    sendToRemote(msg){
        //通知service向对方设备发送信息
        FeatureAbility.callAbility({
            bundleName: 'com.example.funny1024',
            abilityName: 'com.example.funny1024.connect',
            messageCode: 3,
            data: msg,
            abilityType: 0,
            syncOption: 0
        })
    },

    showEnd(){
        //展示结束动画
        this.gameEnd = true
        let ani = Animator.createAnimator({
            fill: 'forwards',
            duration: 1000,
            easing: 'ease',
            begin: 0,end: 1
        })
        ani.onframe = value=>{
            this.endOpacity = value
        }
        ani.play()
    },

    printB(){
        for(let i=0;i<4;i++){
            console.log(JSON.stringify(b_[i]));
        }
    },

    checkIfEnd(){
        //检查有无可合成方块
        let end = true
        for(let i=0;i<4;i++){
            for(let j=0;j<4;j++){
                let v = b_[i][j]
                if(i != 0) end = (end && b_[i-1][j].v != v)
                if(i != 3) end = (end && b_[i+1][j].v != v)
                if(j != 0) end = (end && b_[i][j-1].v != v)
                if(j != 3) end = (end && b_[i][j+1].v != v)
                if(!end) return end
            }
        }
        return end
    }
}
